﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using CalcSharp.Core;
using CalcSharp.Core.Containers;
using CalcSharp.Core.IO;
using CalcSharp.Core.Plugin;
using CalcSharp.Properties;
using CalcSharp.Core.NumberCache;
using System.Collections;
using System.Linq;
using CalcSharp.Core.Memory;

namespace CalcSharp
{
    public enum PlugType
    {
        Command,
        Window
    }

    partial class Program : IPluginHost, IDisposable
    {
        private FloatManager mem;
        private MatrixManager mtrx_mem;
        public Parentform parent;
        private ComplexManager cplx_mem;
        private SetManager set_mem;
        private CFsManager cfs;
        private StringManager str_mem;
        private FlagRegister flags;
        public TimerMan TMan;
        public Dictionary<string, Dictionary<string, string>> paramcompleter;
        public CacheData CacheDB;

        internal void SerializeMem(string path)
        {
            try
            {
                MemoryDumpWriter mwr = new MemoryDumpWriter(path);
                mwr.SerializeAll(ref mem, ref cplx_mem, ref mtrx_mem, ref set_mem, ref str_mem, ref Program.functionmem);
            }
            catch (Exception ex)
            {
                this.TextColor = ConsoleTextColor.TextError;
                ConsoleForm.WriteLine("Memory dump write error: " + ex.Message);
                this.TextColor = ConsoleTextColor.NormalText;
            }
        }

        internal void DeSerializeMem(string path)
        {
            try
            {
                MemoryDumpReader mdr = new MemoryDumpReader(path);
                mdr.DeserializeAllMem(ref mtrx_mem, ref set_mem, ref mem, ref str_mem, ref cplx_mem, ref Program.functionmem);
            }
            catch (Exception ex)
            {
                this.TextColor = ConsoleTextColor.TextError;
                ConsoleForm.WriteLine("Memory dump restore error: " + ex.Message);
                this.TextColor = ConsoleTextColor.NormalText;
            }
        }

        #region IPluginHost Members & Construction

        private uint cmdcount;
        private uint wincount;
        private List<string> CmdPlugAliases;
        private List<ICMDPlugin> CmdPlugs;
        private List<IWindowPlugin> WinPlugs;
        private List<string> WinPlugAliases;

        public string[] WindowPlugins
        {
            get { return WinPlugAliases.ToArray(); }
        }

        public string[] CommandPlugins
        {
            get { return CmdPlugAliases.ToArray(); }
        }

        public string[] PluginCommands
        {
            get
            {
                List<string> Pluglist = new List<string>();
                Pluglist.AddRange(WinPlugAliases);
                Pluglist.AddRange(CmdPlugAliases);
                Pluglist.Sort();
                return Pluglist.ToArray();
            }
        }

        public string this[int i, PlugType t]
        {
            get
            {
                switch (t)
                {
                    case PlugType.Command:
                        if (i < CmdPlugAliases.Count && i >= 0) return CmdPlugAliases[i];
                        else throw new CSException("Plugin Indexing Error.");
                    case PlugType.Window:
                        if (i < WinPlugAliases.Count && i >= 0) return WinPlugAliases[i];
                        else throw new CSException("Plugin Indexing Error.");
                    default:
                        throw new CSException("Plugin Indexing Error.");
                }
            }
        }
        public bool isCommandAvailable(string cmd)
        {
            cmd = cmd.ToUpper(CultureInfo.CurrentCulture);
            return CmdPlugAliases.Contains(cmd);
        }
        public ICMDPlugin GetCMDPluginIface(string cmd)
        {
            int i;
            ICMDPlugin res = new BareCMDPlug();
            bool ok = false;
            string[] aliases;
            for (i = 0; i < this.CmdPlugs.Count; i++)
            {
                if (this.CmdPlugs[i].Name.Contains(";"))
                {
                    aliases = this.CmdPlugs[i].Name.ToUpper(CultureInfo.CurrentCulture).Split(';');
                    var q = aliases.FirstOrDefault(p => p == cmd);
                    if (q != null)
                    {
                        res = CmdPlugs[i];
                        ok = true;
                        break;
                    }
                }
                else if (this.CmdPlugs[i].Name.ToUpper(CultureInfo.CurrentCulture) == cmd)
                {
                    ok = true;
                    res = this.CmdPlugs[i];
                    break;
                }
            }
            if (ok) return res;
            else throw new CSException("Error accesing plugin interface: " + cmd);
        }

        public IWindowPlugin GetWindowPluginIface(string cmd)
        {
            int i;
            IWindowPlugin res = new BareWinPlugin();
            bool ok = false;
            string[] aliases;
            for (i = 0; i < this.CmdPlugs.Count; i++)
            {
                if (this.CmdPlugs[i].Name.Contains(";"))
                {
                    aliases = this.WinPlugs[i].Name.ToUpper(CultureInfo.CurrentCulture).Split(';');
                    var q = aliases.FirstOrDefault(p => p == cmd);
                    if (q != null)
                    {
                        ok = true;
                        res = this.WinPlugs[i];
                        break;
                    }
                }
                else if (this.WinPlugs[i].Name.ToUpper(CultureInfo.CurrentCulture) == cmd)
                {
                    ok = true;
                    res = this.WinPlugs[i];
                    break;
                }
            }
            if (ok) return res;
            else throw new CSException("Error accesing plugin interface: " + cmd);
        }

        public uint CMDCount
        {
            get { return this.cmdcount; }
        }

        public uint WinCount
        {
            get { return this.wincount; }
        }

        public Program(string dir)
        {
            this.parent = new Parentform(); //szülő
            this.mem = new FloatManager();
            this.mtrx_mem = new MatrixManager(MtrxStorageType.tfloat);
            this.cplx_mem = new ComplexManager();
            this.set_mem = new SetManager();
            this.cfs = new CFsManager();
            this.str_mem = new StringManager();
            this.paramcompleter = new Dictionary<string, Dictionary<string, string>>();
            this.flags = new FlagRegister();
            this.CacheDB = new CacheData();
            this.TMan = new TimerMan();
            this.CmdPlugAliases = new List<string>();
            this.WinPlugAliases = new List<string>();
            Type[] ObjType = null;
            string[] pluginFiles = Directory.GetFiles(dir, "plug_*.dll");
            this.CmdPlugs = new List<ICMDPlugin>();
            this.WinPlugs = new List<IWindowPlugin>();
            ICMDPlugin CmdLoaded;
            IWindowPlugin WinLoaded;
            byte[] publickey;
            Object temp = null;
            for (int i = 0; i < pluginFiles.Length; i++)
            {
                string args = pluginFiles[i].Substring(
                    pluginFiles[i].LastIndexOf("\\") + 1,
                    pluginFiles[i].IndexOf(".dll") -
                    pluginFiles[i].LastIndexOf("\\") - 1);
                try
                {
                    Assembly ass = null;
                    ass = Assembly.Load(args);
                    publickey = ass.GetName().GetPublicKeyToken();
                    if (publickey.Length < 8) throw new Exception("Cannnot load plugin, because it isn't signed: "+args);
                    if (ass != null) ObjType = ass.GetTypes();

                }
                catch (Exception ex)
                {
                    if (ex is ReflectionTypeLoadException) MessageBox.Show("Unable to load plugin, because it uses a different API: " + args, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    else MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    //Program.ConsoleForm.WriteLine(ex.Message);
                }
                if (ObjType != null && ObjType.Length > 0)
                {
                    for (int j = 0; j < ObjType.Length; j++)
                    {
                        temp = null;
                        try
                        {
                            temp = Activator.CreateInstance(ObjType[j]);
                        }
                        catch { ; }
                        if (temp is ICMDPlugin)
                        {
                            try
                            {
                                CmdLoaded = (ICMDPlugin)Activator.CreateInstance(ObjType[j]);
                                CmdLoaded.Host = this;
                                CmdPlugs.Add(CmdLoaded);
                                this.paramcompleter.Add(CmdLoaded.Name, CmdLoaded.autocomplete);
                            }
                            catch (Exception ex)
                            {
                                MessageBox.Show("Can't load plugin :" + pluginFiles[i] + "\n Reason: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            }
                        }
                        else if (temp is IWindowPlugin)
                        {
                            try
                            {
                                WinLoaded = (IWindowPlugin)Activator.CreateInstance(ObjType[j]);
                                WinLoaded.Host = this;
                                WinPlugs.Add(WinLoaded);
                                this.paramcompleter.Add(WinLoaded.Name, WinLoaded.autocomplete);
                            }
                            catch (Exception ex)
                            {
                                MessageBox.Show("Can't load plugin :" + pluginFiles[i] + "\n Reason: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            }
                        }
                    }
                }
            }
            Dictionary <string, string> tmp = new Dictionary<string,string>();
            foreach (string cmd in Program.supported)
            {
                tmp = paramsetup.SetupCompleter(cmd);
                if (tmp != null)
                {
                    this.paramcompleter.Add(cmd, tmp);
                }
            }
        }

        public Form Parent
        {
            get 
            {
                return this.parent;
            }
        }

        public MatrixManager Mtrx_mem
        {
            get { return this.mtrx_mem; }
        }

        public FloatManager Mem
        {
            get { return this.mem; }
        }

        public ComplexManager CPLX_mem
        {
            get { return this.cplx_mem; }
        }

        public SetManager Set_mem
        {
            get { return this.set_mem; }
        }

        public StringManager String_mem
        {
            get { return this.str_mem; }
        }

        public bool Register(ICMDPlugin ipi)
        {
            if (ipi.Name.Contains(";"))
            {
                string[] cmds = ipi.Name.ToUpper(CultureInfo.CurrentCulture).Split(';');
                cmdcount += (uint)cmds.Length;
                CmdPlugAliases.AddRange(cmds);
                return true;
            }
            else
            {
                ++cmdcount;
                CmdPlugAliases.Add(ipi.Name.ToUpper(CultureInfo.CurrentCulture));
                return true;
            }
        }

        public bool Register(IWindowPlugin ipi)
        {
            if (ipi.Name.Contains(";"))
            {
                string[] cmds = ipi.Name.ToUpper(CultureInfo.CurrentCulture).Split(';');
                wincount += (uint)cmds.Length;
                WinPlugAliases.AddRange(cmds);
                return true;
            }
            else
            {
                ++wincount;
                WinPlugAliases.Add(ipi.Name.ToUpper(CultureInfo.CurrentCulture));
                return true;
            }
        }

        public CFsManager C_fs
        {
            get { return this.cfs; }
        }

        #endregion
        #region IPluginHost API
        public void write(string str)
        {
            Program.ConsoleForm.Write(str);
        }
        public void writeln(string str)
        {
            Program.ConsoleForm.WriteLine(str);
        }

        public void ExecCommands(string cmd)
        {
            MiscFuncts.RunBlock(cmd);
        }

        public void ClearConsole()
        {
            Program.ConsoleForm.Clear();
        }

        public Tresult RunOneCommand(string cmd)
        {
            int controll = -1;
            Program.cmds = MiscCoreFuncts.split(cmd, false);
            controll = Program.analize(Program.cmds[0]);
            Program.callfunc(controll, false);
            Tresult backup = Program.result;
            Program.result = new Tresult();
            return backup;
        }

        public ConsoleTextColor TextColor
        {
            get
            {
                if (Program.ConsoleForm.ForegroundColor == Settings.Default.console_color) return ConsoleTextColor.NormalText;
                else if (Program.ConsoleForm.ForegroundColor == Settings.Default.console_err) return ConsoleTextColor.TextError;
                else if (Program.ConsoleForm.ForegroundColor == Settings.Default.console_textok) return ConsoleTextColor.TextOK;
                else return ConsoleTextColor.TextWarning;
            }
            set
            {
                switch (value)
                {
                    case ConsoleTextColor.NormalText:
                        Program.ConsoleForm.ForegroundColor = Settings.Default.console_color;
                        break;
                    case ConsoleTextColor.TextError:
                        Program.ConsoleForm.ForegroundColor = Settings.Default.console_err;
                        break;
                    case ConsoleTextColor.TextOK:
                        Program.ConsoleForm.ForegroundColor = Settings.Default.console_textok;
                        break;
                    case ConsoleTextColor.TextWarning:
                        Program.ConsoleForm.ForegroundColor = Settings.Default.console_warn;
                        break;
                }
            }
        }

        public CalculatorModes CalcMode
        {
            get { return Program.mode; }
        }


        public string[] CmdArray
        {
            get { return Program.cmds; }
        }

        public bool CmdExists(string cmd)
        {
            int ctrl = Program.analize(cmd);
            if (ctrl != -1) return true;
            else return false;
        }

        public Version HostVersion
        {
            get
            {
                return Assembly.GetExecutingAssembly().GetName().Version;
            }
        }

        public AMatrix CreateMatrix(int Rows, int Columns)
        {
            return MatrixFuncts.CreateMatrix(Rows, Columns);
        }

        public FlagRegister Flags { get { return this.flags; } }

        #endregion

        #region IDisposable Members

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.mem != null)
                {
                    this.mem.Clear();
                    this.mem = null;
                }
                if (this.mtrx_mem != null)
                {
                    this.mtrx_mem.Clear();
                    this.mtrx_mem = null;
                }
                if (this.cplx_mem != null)
                {
                    this.cplx_mem.Clear();
                    this.cplx_mem = null;
                }
                if (this.C_fs != null)
                {
                    this.C_fs.unsetAll();
                    this.cfs = null;
                }
                if (this.set_mem != null)
                {
                    this.set_mem.Clear();
                    this.set_mem = null;
                }
                if (this.parent != null)
                {
                    this.parent.Dispose();
                    this.parent = null;
                }
                if (this.flags != null)
                {
                    this.flags = null;
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
